#ifndef __CPIL_IMAGE_HPP__
#define __CPIL_IMAGE_HPP__

#include <sharedarray.hpp>
#include <colormodel.hpp>
#include <libgfl.h>

#include <iostream>
using namespace std;

ColorModel *    getColorModelFromGFLLibrary(  const short &);
GFL_BITMAP_TYPE getGFLBitmapTypeFromColorModel(ColorModel* );


template< typename DataType >
class Image : public SharedArray<DataType>
{

    friend class ConstIterator<DataType>;
    friend class      Iterator<DataType>;

    protected:

        unsigned int width;
        unsigned int height;
        ColorModel  * colorModel;

    public:

        Image() : SharedArray<DataType>(), width(0), height(0), colorModel(0) {}
        ~Image() {}
        Image(const Image &img) : SharedArray<DataType>(img), width(img.width), height(img.height),colorModel(img.colorModel) {}
        Image& operator=(const Image &img)
        {
            SharedArray<DataType>::operator = (img);
            width                           = img.width;
            height                          = img.height;
            colorModel                      = img.colorModel;
            return (*this);
        }

        Image(ColorModel * _colorModel) : SharedArray<DataType>(), width(0), height(0), colorModel(_colorModel) {}

        Image(const char *fileName) : SharedArray<DataType>(), width(0), height(0), colorModel()
        {
            load(fileName);
        }

        inline void load(const char *fileName)
        {
            GFL_ERROR error;
            GFL_LOAD_PARAMS load_option;
            GFL_FILE_INFORMATION info;
            GFL_BITMAP * bitmap;

            gflGetDefaultLoadParams(&load_option);
            load_option.LinePadding = 0;

            error = gflLoadBitmap(fileName, &bitmap, &load_option, NULL);
            if(error) throw Exception(gflGetErrorString(error),EXCEPTION_INFOS);
            error = gflGetFileInformation(fileName,-1,&info);
            if(error) throw Exception(gflGetErrorString(error),EXCEPTION_INFOS);

            colorModel = getColorModelFromGFLLibrary(info.ColorModel);
            if( !colorModel )
                throw Exception("This color model is not available.",EXCEPTION_INFOS);
            width  = bitmap->Width;
            height = bitmap->Height;

            unsigned int  _size(bitmap->Width*bitmap->Height*colorModel->getChannels()), n(0);
            SharedArray<DataType>::setSize(_size);
            GFL_UINT8   * src(bitmap->Data);
            DataType    * dst(this->data);
            for(; n < _size ; n++ )
                *(dst++) = *(src++);
            gflFreeBitmap(bitmap);
        }

        inline void save(const char *fileName, const char *format, const short &quality=100)
        {
            GFL_ERROR error;
            GFL_SAVE_PARAMS save_option;
            GFL_BITMAP * bitmap;
            GFL_BITMAP_TYPE bmpType = getGFLBitmapTypeFromColorModel(colorModel);

            if(!bmpType)
                throw Exception("This color model is not writable.",EXCEPTION_INFOS);
            if( !gflFormatIsSupported(format) )
                throw Exception("This format is not supported.",EXCEPTION_INFOS);

            bitmap = gflAllockBitmap(bmpType,width,height,colorModel->getChannels(),0);
            if(!bitmap) throw Exception("Impossible to alloc a new bitmap",EXCEPTION_INFOS);

            unsigned int  _size(bitmap->Width*bitmap->Height*colorModel->getChannels()), n(0);
            GFL_UINT8   * dst(bitmap->Data);
            DataType    * src(this->data);
            for(; n < _size ; n++ )
                *(dst++) = *(src++);

            gflGetDefaultSaveParams( &save_option );

            save_option.Flags = GFL_SAVE_REPLACE_EXTENSION;
            save_option.FormatIndex = gflGetFormatIndexByName(format);
            save_option.Quality = quality;

            error = gflSaveBitmap((char*)fileName, bitmap, &save_option);
            if(error) throw Exception(gflGetErrorString(error),EXCEPTION_INFOS);
            gflFreeBitmap(bitmap);
        }

        inline unsigned int    getWidth() const { return width; }
        inline unsigned int   getHeight() const { return height; }
        inline unsigned int channels() const { return ((colorModel)?colorModel->getChannels():1); }
        inline unsigned int nbPixels() const { return width*height*channels(); }

        inline void         setColorModel(GREY * greyColorSpace)
        {
            //if( colorModel )
            //    colorModel->convert(*this,greyColorSpace);
        }

        inline ColorModel * getColorModel() const { return colorModel; }


        inline void setSize(unsigned int _width, unsigned int _height, ColorModel * _colorModel=FREE_SPACE)
        {
            width      = _width;
            height     = _height;
            colorModel = _colorModel;
            SharedArray<DataType>::setSize(width*height*channels());
        }


};

#endif
